package com.cqjtu.eecs.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.cqjtu.eecs.entity.CourseGoal;
import com.cqjtu.eecs.entity.CourseGoalScore;
import com.cqjtu.eecs.entity.dto.WeightDto;
import com.cqjtu.eecs.mapper.CourseGoalMapper;
import com.cqjtu.eecs.mapper.CourseGoalScoreMapper;
import com.cqjtu.eecs.service.ICourseGoalAssessService;
import com.cqjtu.eecs.service.ICourseGoalScoreService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.cqjtu.eecs.service.IProjectCourseService;
import com.cqjtu.eecs.service.IStudentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.function.BiConsumer;
import java.util.stream.Collectors;

/**
 * <p>
 * 课程目标-成绩 服务实现类
 * </p>
 *
 * @author    
 * @since 2022-03-07
 */
@Service
public class CourseGoalScoreServiceImpl extends ServiceImpl<CourseGoalScoreMapper, CourseGoalScore> implements ICourseGoalScoreService {

    @Autowired
    private IStudentService studentService;

    @Autowired
    private ICourseGoalAssessService courseGoalAssessService;

    @Autowired
    private IProjectCourseService projectCourseService;

    @Autowired
    private CourseGoalMapper courseGoalMapper;

    /**
     * 计算课程目标达成度
     * @param grade 年级
     * @param courseName 课程名
     * @param tcId 教学班编号
     * @param headMap excel头部信息
     * @param dataList 学生成绩
     * @return
     */
    @Override
    public boolean calculateAndSave(Integer grade, String courseName, Integer tcId, Map<Integer, String> headMap, List<Map<Integer, String>> dataList) {
        try {
            // 获取所有专业名
            Set<String> majorNames = new HashSet<>();
            dataList.forEach(item -> {
                majorNames.add(item.get(2));
            });

            // 各专业课程目标考核环节权重
            Map<String, Map<Integer, Map<String, WeightDto>>> majorCourseGoalMap = new HashMap<>();
            // 各专业课程目标支撑毕业要求权重
            Map<String, Map<Integer, Float>> courseGoalRequirement = new HashMap<>();
            majorNames.forEach(majorName -> {
                Map<Integer, Map<String, WeightDto>> courseGoalLinkMap = courseGoalAssessService.getCourseGoalLinkMap(grade, courseName, majorName);
                majorCourseGoalMap.put(majorName, courseGoalLinkMap);
                Integer pcId = projectCourseService.getProjectCourseId(grade, majorName, courseName);
                Map<Integer, Float> courseGoalWeightMap = courseGoalMapper.selectList(new QueryWrapper<CourseGoal>().eq("project_course_id", pcId))
                        .stream().collect(Collectors.toMap(CourseGoal::getCourseGoalId, CourseGoal::getWeight));
                courseGoalRequirement.put(majorName, courseGoalWeightMap);
            });

            // 学生学号集合
            List<String> codeList = new ArrayList<>();
            dataList.forEach(item -> {
                codeList.add(item.get(0));
            });

            //学号id映射
            Map<String, Integer> codeIdMap = studentService.getCodeIdMap(codeList);

            List<CourseGoalScore> courseGoalScoreList = new ArrayList<>();
            // 遍历每一位学生
            dataList.forEach(score -> {
                // 该学生所在专业
                String majorName = score.get(2);

                // 获取该专业下该课程的所有课程目标和考核环节映射
                Map<Integer, Map<String, WeightDto>> courseGoals = majorCourseGoalMap.get(majorName);
                Set<Integer> courseGoalId = courseGoals.keySet();

                // 将已经计算过的目标达成度保存起来
                Map<Integer, Float> targetDegreeMap = new HashMap<>();

                // 遍历该课程的所有的课程目标
                for (Integer id: courseGoalId){
                    CourseGoalScore courseGoalScore = new CourseGoalScore();
                    courseGoalScore.setCourseGoalId(id);
                    courseGoalScore.setSid(codeIdMap.get(score.get(0)));
                    courseGoalScore.setTcId(tcId);

                    // 根据课程目标编号获取支撑该课程目标的环节及其权重
                    Map<String, WeightDto> weightDtoMap = courseGoals.get(id);

                    // 该课程目标 目标达成度是否计算过
                    Float targetDegree = targetDegreeMap.get(id);
                    if (targetDegree == null) {
                        // 目标达成度
                        targetDegree = this.calTargetDegree(weightDtoMap);
                        courseGoalScore.setTargetDegree(targetDegree);
                        targetDegreeMap.put(id, targetDegree);
                    } else {
                        courseGoalScore.setTargetDegree(targetDegree);
                    }

                    // 达成值
                    float value = this.calValue(headMap, score, weightDtoMap);
                    courseGoalScore.setVal(value);

                    // 达成度
                    float degree = value / targetDegree;
                    courseGoalScore.setDegree(degree);

                    // 根据专业课程目标编号获取课程目标对毕业要求的支撑权重
                    Float weight = courseGoalRequirement.get(majorName).get(id);
                    Float contributionValue = weight * degree;
                    courseGoalScore.setContributionValue(contributionValue);

                    courseGoalScoreList.add(courseGoalScore);
                }
            });
            // 批量插入考核环节成绩
            boolean saveBatch = this.saveBatch(courseGoalScoreList);
            if (saveBatch)
                return true;

        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    // 计算达成值
    private float calValue(Map<Integer, String> headMap, Map<Integer, String> score, Map<String, WeightDto> weightDtoMap) {
        int size = score.size();
        float value = 0f;
        for (int i = 3; i <= size - 2; i++) {
            String linkName = headMap.get(i);
            WeightDto weightDto = weightDtoMap.get(linkName);
            if (weightDto == null)
                continue;
            value += (Float.parseFloat(score.get(i)) * weightDto.getBackWeight() * weightDto.getLinkWeight());
        }
        return value;
    }

    // 计算目标达成值
    private float calTargetDegree(Map<String, WeightDto> weightDtoMap) {
        Set<String> weights = weightDtoMap.keySet();
        float targetDegree = 0f;
        for (String key : weights) {
            WeightDto weightDto = weightDtoMap.get(key);
            targetDegree += (100.0 * weightDto.getLinkWeight() * weightDto.getBackWeight());
        }
        return targetDegree;
    }
}
